const COMMANDS = {
  C_ARITHMATIC: 0,
  C_PUSH: 1,
  C_POP: 2,
  C_LABEL: 3,
  C_GOTO: 4,
  C_IF: 5,
  C_FUNCTION: 6,
  C_RETURN: 7,
  C_CALL: 8
};

const ARITHMATIC_COMMANDS = {
  ADD: 'add',
  SUB: 'sub',
  NEG: 'neg',
  EQ: 'eq',
  GT: 'gt',
  LT: 'lt',
  AND: 'and',
  OR: 'or',
  NOT: 'not'
};

const SEGMENTS = {
  ARGUMENT: 'argument',
  LOCAL: 'local',
  STATIC: 'static',
  CONSTANT: 'constant',
  THIS: 'this',
  THAT: 'that',
  POINTER: 'pointer',
  TEMP: 'temp'
};

class Parser {
  constructor(littleIO) {
    this.littleIO = littleIO;
    this.currentCommand = '';
    this._commandType = null;
    this._arg1 = '';
    this._arg2 = 0;
  }

  hasMoreLines() {
    return this.littleIO.hasMoreLines();
  }

  advance() {
    if (!this.hasMoreLines()) return;
    this.currentCommand = this.littleIO.advance();
    this.parseParts();
    return this.currentCommand;
  }

  parseParts() {
    if (!this.currentCommand) return;

    if (/^push/.test(this.currentCommand)) {
      this._commandType = COMMANDS.C_PUSH;
    } else if (/^pop/.test(this.currentCommand)) {
      this._commandType = COMMANDS.C_POP;
    } else if (/^label/.test(this.currentCommand)) {
      this._commandType = COMMANDS.C_LABEL;
    } else if (/^goto/.test(this.currentCommand)) {
      this._commandType = COMMANDS.C_GOTO;
    } else if (/^if/.test(this.currentCommand)) {
      this._commandType = COMMANDS.C_IF;
    } else if (/^function/.test(this.currentCommand)) {
      this._commandType = COMMANDS.C_FUNCTION;
    } else if (/^call/.test(this.currentCommand)) {
      this._commandType = COMMANDS.C_CALL;
    } else if (/^return/.test(this.currentCommand)) {
      this._commandType = COMMANDS.C_RETURN;
    } else if (Object.values(ARITHMATIC_COMMANDS).includes(this.currentCommand)) {
      this._commandType = COMMANDS.C_ARITHMATIC;
    }
    this.parsePushOrPopArgs();
  }

  parsePushOrPopArgs() {
    const commandArr = this.currentCommand.split(/\s+/);
    if (commandArr.length === 1) {
      this._arg1 = commandArr[0];
    } else if (commandArr.length === 2) {
      this._arg1 = commandArr[1];
    } else if (commandArr.length === 3) {
      const [operand, segment, index] = this.currentCommand.split(/\s+/);
      this._arg1 = segment;
      this._arg2 = parseInt(index);
    }
  }

  isPushOrPopCommand() {
    return this.commandType() === COMMANDS.C_POP || this.commandType() === COMMANDS.C_PUSH;
  }

  isLabelCommand() {
    return this.commandType() === COMMANDS.C_LABEL;
  }

  isGotoCommand() {
    return this.commandType() === COMMANDS.C_GOTO;
  }

  isIfGotoCommand() {
    return this.commandType() === COMMANDS.C_IF;
  }

  isFunctionCommand() {
    return this.commandType() === COMMANDS.C_FUNCTION;
  }

  isReturnCommand() {
    return this.commandType() === COMMANDS.C_RETURN;
  }

  isCallCommand() {
    return this.commandType() === COMMANDS.C_CALL;
  }

  commandType() {
    return this._commandType;
  }

  arg1() {
    return this._arg1;
  }

  arg2() {
    return this._arg2;
  }
}

module.exports.Parser = Parser;
module.exports.COMMANDS = COMMANDS;
module.exports.SEGMENTS = SEGMENTS;
module.exports.ARITHMATIC_COMMANDS = ARITHMATIC_COMMANDS;
